﻿using System;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace MassiveBattle.Manager.Editor
{
	public class BaseManagerEditor : UnityEditor.Editor
	{
		protected void ShowEnableMenu(ref bool isActive, string text, Action func)
		{
			isActive = EditorGUILayout.ToggleLeft(text, isActive);
			GUI.enabled = isActive;
			if (func != null)
				func();
			GUI.enabled = true;
		}

		protected void ShowLabelField(string text)
		{
			EditorGUILayout.LabelField(text);
		}

		private void ShowObjectField<T>(string name, ref T value, bool isAllowSceneObject = true) where T : UnityEngine.Object
		{
			value = EditorGUILayout.ObjectField(name, value, typeof(T), isAllowSceneObject) as T;
		}

		protected void Indent()
		{
			EditorGUI.indentLevel++;
		}

		protected void Outdent()
		{
			EditorGUI.indentLevel--;
		}

		protected void ShowSetting(BaseManager inst, BaseManager.CheatData cheat)
		{
			if (!EditorApplication.isPlayingOrWillChangePlaymode)
			{
				ShowLabelField("기본 세팅");
				Indent();
				cheat.loginAccount = EditorGUILayout.TextField("로그인 계정", cheat.loginAccount);
				EditorGUILayout.PropertyField(serializedObject.FindProperty("formationPositionRoot"), new GUIContent("진형 위치"));
				ShowObjectField("페이즈 위치", ref inst.phasePositionRoot);
				Outdent();
				EditorGUILayout.Space();

				ShowLabelField("카메라 세팅");
				Indent();
				EditorGUILayout.PropertyField(serializedObject.FindProperty("cameraItpTime"), new GUIContent("보간 시간"));
				{
					var property = serializedObject.FindProperty("cameraList");
					property.arraySize = 3;

					EditorGUILayout.PropertyField(property.GetArrayElementAtIndex(0), new GUIContent("Camera"));
					EditorGUILayout.PropertyField(property.GetArrayElementAtIndex(1), new GUIContent("MiddleCam"));
					EditorGUILayout.PropertyField(property.GetArrayElementAtIndex(2), new GUIContent("MainCamera"));
				}
				EditorGUILayout.PropertyField(serializedObject.FindProperty("takePlayOrder"), new GUIContent("테이크 순서"), true);
				Outdent();
				EditorGUILayout.Space();

				ShowLabelField("전술 이펙트 세팅");
				Indent();
				EditorGUILayout.PropertyField(serializedObject.FindProperty("effectTacticsPosition"), new GUIContent("위치"));
				{
					var property = serializedObject.FindProperty("effectTacticsList");
					property.arraySize = 3;

					EditorGUILayout.PropertyField(property.GetArrayElementAtIndex(0), new GUIContent("치료"));
					EditorGUILayout.PropertyField(property.GetArrayElementAtIndex(1), new GUIContent("방어"));
					EditorGUILayout.PropertyField(property.GetArrayElementAtIndex(2), new GUIContent("공격"));
				}
				Outdent();
				EditorGUILayout.Space();

				ShowLabelField("상태이상 이펙트 세팅");
				Indent();
				ShowObjectField("스턴/마비", ref inst.EffectStateList[0]);
				Outdent();
				EditorGUILayout.Space();

				ShowLabelField("피격시 이펙트 세팅");
				Indent();
				ShowObjectField("재질", ref inst.EffectHitMaterial);
				inst.EffectHitTime = EditorGUILayout.FloatField("시간(초)", inst.EffectHitTime);
				Outdent();
				EditorGUILayout.Space();
			}
		}

		protected virtual void ShowTester(BaseManager inst, BaseManager.CheatData cheat) { }

		protected virtual void ShowCheat(BaseManager inst, BaseManager.CheatData cheat)
		{
			ShowLabelField("치트 세팅");
			Indent();
			cheat.isTester = EditorGUILayout.ToggleLeft(string.Format("테스트 환경 : {0}", cheat.isTester ? "켜짐" : "꺼짐"), cheat.isTester);
			if (EditorApplication.isPlaying)
			{
				ShowGameControl(inst, cheat);

				ShowPhase(inst);
			}
			if (!EditorApplication.isPlayingOrWillChangePlaymode)
			{
				ShowBossId(inst, cheat);

				ShowEnableMenu(ref cheat.isActionTimeInitial, string.Format("초기 쿨타임 범위(초)[{0:0.##}] : {1:0.#} ~ {2:0.#}", cheat.actionTimeInitialMax - cheat.actionTimeInitialMin, cheat.actionTimeInitialMin, cheat.actionTimeInitialMax), () =>
				{
					EditorGUILayout.MinMaxSlider(ref cheat.actionTimeInitialMin, ref cheat.actionTimeInitialMax, 0f, 10f);
				});
			}
			ShowEnableMenu(ref cheat.isActionTime, string.Format("쿨타임 범위(초)[{0:0.##}] : {1:0.#} ~ {2:0.#}", cheat.actionTimeMax - cheat.actionTimeMin, cheat.actionTimeMin, cheat.actionTimeMax), () =>
			{
				EditorGUILayout.MinMaxSlider(ref cheat.actionTimeMin, ref cheat.actionTimeMax, 0f, 10f);
			});
			ShowEnableMenu(ref cheat.isDamagePlayer, string.Format("아군 데미지 범위[{0}] : {1} ~ {2}", cheat.damagePlayerMax - cheat.damagePlayerMin, cheat.damagePlayerMin, cheat.damagePlayerMax), () =>
			{
				EditorGUILayout.MinMaxSlider(ref cheat.damagePlayerMin, ref cheat.damagePlayerMax, 0f, 250f);
				cheat.damagePlayerMin = Mathf.FloorToInt(cheat.damagePlayerMin);
				cheat.damagePlayerMax = Mathf.FloorToInt(cheat.damagePlayerMax);
			});
			ShowEnableMenu(ref cheat.isDamageEnemy, string.Format("적군 데미지 범위[{0}] : {1} ~ {2}", cheat.damageEnemyMax - cheat.damageEnemyMin, cheat.damageEnemyMin, cheat.damageEnemyMax), () =>
			{
				EditorGUILayout.MinMaxSlider(ref cheat.damageEnemyMin, ref cheat.damageEnemyMax, 0f, 250f);
				cheat.damageEnemyMin = Mathf.FloorToInt(cheat.damageEnemyMin);
				cheat.damageEnemyMax = Mathf.FloorToInt(cheat.damageEnemyMax);
			});
			ShowEnableMenu(ref cheat.isSkillProbPlayer, string.Format("아군 스킬 발동 확률"), () =>
			{
				cheat.skillProbPlayer = Mathf.Clamp01(EditorGUILayout.FloatField("확률(%)", cheat.skillProbPlayer * 100f) * 0.01f);
			});
			ShowEnableMenu(ref cheat.isSkillProbEnemy, string.Format("적군 스킬 발동 확률"), () =>
			{
				cheat.skillProbEnemy = Mathf.Clamp01(EditorGUILayout.FloatField("확률(%)", cheat.skillProbEnemy * 100f) * 0.01f);
			});
			Outdent();

			if (inst.DataList != null)
			{
				EditorGUILayout.Space();

				ShowLabelField("배치 상황");
				Indent();
				for (int i = 0; i < inst.DataList.Length; i++)
				{
					EditorGUILayout.BeginHorizontal();
					ShowLabelField(string.Format("{0} - {1}/{2}", (ESide)i, inst.DataList[i].HealthPointCur, inst.DataList[i].HealthPoint));
					if (GUILayout.Button("삭제"))
					{
						foreach (var controller in inst.DataList[i].CharacterList)
							controller.Damage(int.MaxValue, false, null);
					}
					EditorGUILayout.EndHorizontal();
					Indent();
					for (int j = 0; j < (int)ELine.END; j++)
					{
						EditorGUILayout.BeginHorizontal();
						ShowLabelField(string.Format("{0}", (ELine)j));
						if (GUILayout.Button("삭제"))
						{
							foreach (var controller in inst.DataList[i].CharacterList.Where(data => data.IsAlive && data.Line == (ELine)j))
								controller.Damage(int.MaxValue, false, null);
						}
						EditorGUILayout.EndHorizontal();
						Indent();
						for (int k = 0; k < 8; k++)
						{
							var controller = inst.DataList[i].CharacterList.FirstOrDefault(data => data.IsAlive && data.Line == (ELine)j && data.Pos == k);

							EditorGUILayout.BeginHorizontal();
							GUI.enabled = controller != null;
							GameObject gameObject = controller != null ? controller.ModelGameObject : null;
							ShowObjectField(string.Format("{0}[{1:0.0}]", k, controller != null ? controller.ActionTimeLeft : 0f), ref gameObject, false);
							if (GUILayout.Button("삭제"))
								controller.Damage(int.MaxValue, false, null);
							GUI.enabled = true;
							EditorGUILayout.EndHorizontal();
						}
						Outdent();
					}
					Outdent();
				}
				Outdent();
			}
		}

		protected void ShowGameControl(BaseManager inst, BaseManager.CheatData cheat)
		{
			EditorGUILayout.BeginHorizontal();
			ShowLabelField("게임 제어");
			if (GUILayout.Button("시작"))
				inst.StartBattle();
			EditorGUILayout.EndHorizontal();
			if (inst.IsActive != EditorGUILayout.ToggleLeft(string.Format("게임 상태 : {0}", inst.IsActive ? "실행 중" : "멈춤"), inst.IsActive))
			{
				if (inst.IsActive)
					inst.PauseBattle();
				else
					inst.ResumeBattle();
			}
		}

		protected virtual void ShowPhase(BaseManager inst) { }

		protected virtual void ShowBossId(BaseManager inst, BaseManager.CheatData cheat) { }

		public override void OnInspectorGUI()
		{
			if (EditorApplication.isCompiling)
			{
				ShowLabelField("컴파일 중...");

				return;
			}

			serializedObject.Update();

			var inst = target as BaseManager;

			ShowSetting(inst, inst.cheat);

			ShowTester(inst, inst.cheat);

			ShowCheat(inst, inst.cheat);

			if (GUI.changed)
			{
				serializedObject.ApplyModifiedProperties();

				EditorUtility.SetDirty(target);
			}

			if (EditorApplication.isPlaying)
				Repaint();
		}
	}
}
